package cz.possoft.client.clovece.gui.components;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

import javax.swing.JTextPane;

import cz.possoft.client.clovece.gui.panel.ClovecePanel;
import cz.possoft.client.utils.PanelHolderUtils;
import cz.possoft.generic.utils.GenericUtils;

/**
 * Pane where is cube painted.
 * 
 * @author <a href="mailto:mposolda@gmail.com">Marek Posolda</a>
 */
public class CubePane extends JTextPane implements Runnable
{
   // This is true if there is 'ring' (red border) around this pane. This means that player has it's move and mouse is under the pane. 
   private boolean ring = false;
   
   // Current state of cube (1-6)
   private int state = 0;
   
   // Required number, which should be result of playing with cube (Result of playing is known before playing with cube starts. Cube is only visual effect).
   private int requiredNumber = 0;
   
   // flag is true when playing with cube is in progress
   private boolean runFlag = false;
   
   // helper lock
   private Object lock = new Object(); 
   
   // helper CountDownLatch used for synchronization with caller
   private CountDownLatch pomLatch;
   
   // Helper constants for playing with cube effect.
   private static final int SPEED1 = 50;
   private static final int SPEED2 = 150;
   private static final int TOTAL_COUNTER = 5;
   private static final int COUNTER_FOR_CHANGING_SPEED = 4;         
   
   
   /**
    * Do necessary operations when initializing this component (Init listeners and start thread.)
    *
    */
   public void initialize()
   {      
      addMouseListener(new CubePaneMouseListener());
      new Thread(this).start();
   }
   
   // Painting of whole pane  
   public void paint(Graphics g)  
   {
      super.paint(g);
         
      // Choose color according to ring
      if (ring)
      {
         g.setColor(Color.RED);
      }
      else
      {
         g.setColor(Color.BLACK);
      }
      
      // border
      g.fillRect(0,0,100,100);
      
      // inner part
      g.setColor(Color.WHITE);
      g.fillRect(5,5,90,90);
      
      g.setColor(Color.BLACK);      
      
      // draw circles according to current state of cube
      switch (state)
      {
         case 1: fillCircle(40, 40, g);
                 break;
                 
         case 2: fillCircle(15, 15, g);         
                 fillCircle(65, 65, g);
                 break;
                 
         case 3: fillCircle(15, 15, g);
                 fillCircle(40, 40, g);
                 fillCircle(65, 65, g);
                 break;
                 
         case 4: fillCircle(15, 15, g);
                 fillCircle(15, 65, g);
                 fillCircle(65, 15, g);       
                 fillCircle(65, 65, g);
                 break;
                 
         case 5: fillCircle(15, 15, g);
                 fillCircle(15, 65, g);
                 fillCircle(65, 15, g);       
                 fillCircle(65, 65, g);
                 fillCircle(40, 40, g);
                 break;      
                 
         case 6: fillCircle(15, 15, g);
                 fillCircle(15, 65, g);
                 fillCircle(65, 15, g);       
                 fillCircle(65, 65, g);
                 fillCircle(15, 40, g);
                 fillCircle(65, 40, g);
                 break;                    
      }
         
   }   
   
   /**
    * Starting the playing with cube. Results of playing will be 'number'.
    * Idea is that we know result before playing with cube is finished. So playing of cube is only visual effect.
    * 
    * @param number result of playing with cube.
    * @param latch latch
    */
   public void playCube(int number, CountDownLatch latch)
   {      
      // init instance variables before playing
      pomLatch = latch;
      requiredNumber = number;
      runFlag = true;      
      
      // notify thread to init playing with cube
      synchronized (lock)
      {
         lock.notify();
      }
   }
   
   /**
    * getter for current state on cube (Last number, which was played on cube)
    * 
    * @return current state
    */
   public int getStateOnCube()
   {
      return requiredNumber;
   }
   
   /**
    * Running of thread
    */
   public void run() 
   {
      while (true) 
      {
         try 
         {
            synchronized(lock) 
            {
                  lock.wait();
            }
         } 
         catch (Exception e) 
         {
            e.printStackTrace();
         }
          
         // init variables
         int counter = 0;
         int speedOfCube = SPEED1;
         Random rand = new Random();
         
         // Playing with cube is in progress
         while (runFlag) 
         {  
            // sleep some time
            GenericUtils.safeSleep(speedOfCube);
            
            synchronized (lock)
            {
               // Compute next random int for cube
               state = rand.nextInt(6) + 1;                   
               
               // if required number is result of 'cube' for 'counter' times, then playing of cube is finished.
               if (state == requiredNumber)
               {
                  counter++;
                  if (counter == COUNTER_FOR_CHANGING_SPEED)
                  {
                     // slower speed when playing of cube is going to finish (Visual effect)
                     speedOfCube = SPEED2;
                  }
                  else if (counter == TOTAL_COUNTER)
                  {
                     runFlag = false;
                     
                     // notify caller
                     pomLatch.countDown();
                     pomLatch = null;
                  }
               }
            }
            
            // repaint the pane with cube
            repaint();            
         }
         
      }
   }   
   
   /**
    * Fill one small circle on cube.
    *      
    * @param x
    * @param y
    * @param g
    */
   private void fillCircle(int x, int y, Graphics g)
   {
      g.fillArc(x, y, 20, 20, 0, 360);
   } 
   
   /**
    * 
    * Helper listener class for handling mouse events.
    */
   private class CubePaneMouseListener extends MouseAdapter
   {
      /**
       * This happen when mouse is entered the space under the pane.
       */
      public void mouseEntered(MouseEvent e) 
      {
         if (!(PanelHolderUtils.getCurrentGamePanel() instanceof ClovecePanel)) 
         {
            return;
         }
         ClovecePanel clovecePanel = (ClovecePanel)PanelHolderUtils.getCurrentGamePanel();
         
         if (clovecePanel.getStateOfGame() == 1)
         {
            ring = true;   
            repaint();            
         }
      }
      
      /**
       * This happen when mouse is exited from space under the pane.
       */      
      public void mouseExited(MouseEvent e) 
      {         
         if (ring)
         {
            ring = false;   
            repaint();            
         }
      }      
      
      /**
       * This happen when mouse is pressed under the pane.
       */
      public void mousePressed(MouseEvent e) 
      {
         if (!(PanelHolderUtils.getCurrentGamePanel() instanceof ClovecePanel)) 
         {
            return;
         }
         final ClovecePanel clovecePanel = (ClovecePanel)PanelHolderUtils.getCurrentGamePanel();
         
         if (clovecePanel.getStateOfGame() == 1)
         {
            // We don't want to trigger playing event if it's already in progress
            synchronized (lock)
            {
               if (runFlag)
               {
                  return;
               }
            }
            
            // trigger of playing with cube. We need to trigger it in new Thread, because playing with cube in UI is long running operation
            // and we need UI to handle events.
            new Thread()
            {
               
               public void run()
               {
                  clovecePanel.playWithCube();
               }
               
            }.start();               
         }         
      }      
   }   

}

